#include "ResourceDatabase.h"

#include "boost_filesystem.h"
#include "TinyXML\tinyxml.h"
#include "TinyXML\tinystr.h"

#include "Conversions.h"
#include "Item.h"
#include "Logger.h"
#include "Singleton.h"
#include "ResImage.h"
#include "ResSound.h"

bool ResourceDatabase::initialised = false;
ResourceDatabase ResourceDatabase::instance = ResourceDatabase();

void ResourceDatabase::initialise(std::string savegame)
{
    BEGIN_INITIALISATION("Initialising ResourceDatabase...");

    // load entries from file
    if(!instance.readFile(savegame))
        {return;}

    END_INITIALISATION("... ResourceDatabase initialised.");
}

ResourceDatabase& ResourceDatabase::get()
{
    GET_INSTANCE("ResourceDatabase");
}

void ResourceDatabase::cleanUpAll()
{
    BEGIN_CLEAN_UP("ResourceDatabase/Images");
    instance.images.clear();
    END_CLEAN_UP("ResourceDatabase/Images");

    BEGIN_CLEAN_UP("ResourceDatabase/Sounds");
    instance.sounds.clear();
    END_CLEAN_UP("ResourceDatabase/Sounds");
}

ResourceDatabase::ResourceDatabase()
{
}

void ResourceDatabase::deleteImage( std::string filename )
{

}

void ResourceDatabase::deleteImage( int id )
{

}

void ResourceDatabase::addImage( int id, int rows, int columns, std::string filename )
{
    images.insert(std::pair<int, ResImagePtr>(id, ResImagePtr(new ResImage(filename, columns, rows))));
}

void ResourceDatabase::addSound( int id, std::string filename, char flags, sf::Vector2i position, float volume /*= 1.0f*/, float pitch /*= 1.0f*/ )
{
    sounds.insert(std::pair<int, ResSoundPtr>(id, ResSoundPtr(new ResSound(filename, flags, position, volume, pitch))));
}

void ResourceDatabase::deleteSound( int id )
{

}

void ResourceDatabase::deleteSound( std::string filename )
{

}

void ResourceDatabase::cleanUpSounds()
{
    BEGIN_CLEAN_UP("ResourceDatabase/Sounds");
    instance.sounds.clear();
    END_CLEAN_UP("ResourceDatabase/Sounds");
}

void ResourceDatabase::update( float delta )
{
    for(sound_map::const_iterator it = sounds.begin();it != sounds.end(); ++it)
    {
        (*it).second->update(delta);
    }
}

void ResourceDatabase::cleanUpImages()
{
    BEGIN_CLEAN_UP("ResourceDatabase/Images");
    instance.images.clear();
    END_CLEAN_UP("ResourceDatabase/Images");
}

const ResSoundPtr ResourceDatabase::getSound( int id )
{
    sound_map::const_iterator item = sounds.find(id);
    if(item == sounds.end())
    {
        throw std::runtime_error("Invalid sound ID (" +
            Conversions::toString(8) +
            ") in ResourceDatabase::getSound().");
    }
    return item->second;
}

bool ResourceDatabase::readFile( std::string saveName )
{
    std::string rdbPath = "xml/" + saveName + "/ResourceDatabase.xml";
    TiXmlDocument rdDoc(rdbPath.c_str());
    if (!rdDoc.LoadFile())
    {
        throw std::runtime_error(rdbPath + " can not be opened.");
        return false;
    }
    int imagesSize;
    int soundsSize;
    TiXmlElement imagesElement = *rdDoc.RootElement()->FirstChildElement("Images");
    TiXmlElement soundsElement = *rdDoc.RootElement()->FirstChildElement("Sounds");
    imagesElement.Attribute("size", &imagesSize);
    soundsElement.Attribute("size", &soundsSize);

    TiXmlElement* imageElement = imagesElement.FirstChildElement("Image");
    for(imageElement; imageElement; imageElement = imageElement->NextSiblingElement())
    {
        int id, columns, rows; std::string filePath;
        imageElement->QueryIntAttribute("id", &id);
        imageElement->QueryIntAttribute("rows", &rows);
        imageElement->QueryIntAttribute("columns", &columns);
        imageElement->QueryStringAttribute("filePath", &filePath);
        addImage(id, rows, columns, filePath);
    }    
    TiXmlElement* soundElement = soundsElement.FirstChildElement("Sound");
    for(soundElement; soundElement; soundElement = soundElement->NextSiblingElement())
    {
        std::string filePath; char flags; int id, positionX, positionY; float volume, pitch;
        soundElement->QueryIntAttribute("id", &id);
        soundElement->QueryStringAttribute("filePath", &filePath);
        soundElement->QueryValueAttribute("flags", &flags);
        soundElement->QueryIntAttribute("positionX", &positionX);
        soundElement->QueryIntAttribute("positionY", &positionY);
        soundElement->QueryFloatAttribute("volume", &volume);
        soundElement->QueryFloatAttribute("pitch", &pitch);
        addSound(id, filePath, flags, sf::Vector2i(positionX, positionY), volume, pitch);
    }
    return true;
}


void ResourceDatabase::writeFile( std::string saveName ) const
{
    TiXmlDocument rdDoc;
    TiXmlDeclaration* declaration = new TiXmlDeclaration("1.0", "", "");
    rdDoc.LinkEndChild(declaration);
    //Root element
    TiXmlElement* rdbElement = new TiXmlElement("RESOURCE_DATABASE");
    rdDoc.LinkEndChild(rdbElement);
    //images map
    TiXmlElement* imagesElement = new TiXmlElement("Images");
    imagesElement->SetAttribute("size", images.size());
    for (img_map::const_iterator item = images.begin();item != images.end(); ++item)
    {
        TiXmlElement* imgElement = new TiXmlElement("Image");
        imgElement->SetAttribute("id", item->first);
        imgElement->SetAttribute("filePath", item->second->getFileName().c_str());
        imgElement->SetAttribute("rows", item->second->getRows());
        imgElement->SetAttribute("columns", item->second->getColumns());
        imagesElement->LinkEndChild(imgElement);
    }
    rdbElement->LinkEndChild(imagesElement);
    //sounds map
    TiXmlElement* soundsElement = new TiXmlElement("Sounds");
    soundsElement->SetAttribute("size", sounds.size());
    for (sound_map::const_iterator item = sounds.begin();item != sounds.end(); ++item)
    {
        TiXmlElement* soundElement = new TiXmlElement("Sound");
        soundElement->SetAttribute("id", item->first);
        soundElement->SetAttribute("filePath", item->second->getFileName().c_str());
        soundElement->SetAttribute("flags", item->second->getFlags());
        soundElement->SetAttribute("positionX", item->second->getPosition().x);
        soundElement->SetAttribute("positionY", item->second->getPosition().y);
        soundElement->SetAttribute("volume", item->second->GetVolume());
        soundElement->SetAttribute("pitch", item->second->GetPitch());
        soundsElement->LinkEndChild(soundElement);
    }
    rdbElement->LinkEndChild(soundsElement);
    std::string pathSave = "xml/" + saveName + "/ResourceDatabase.xml";
    rdDoc.SaveFile(pathSave.c_str());
}

void ResourceDatabase::save(std::string saveName) const
{
    writeFile(saveName);
}

const ResImagePtr ResourceDatabase::getImage( int id )
{
    img_map::const_iterator item = images.find(id);
    if(item == images.end())
    {
        throw std::runtime_error("Invalid image ID (" +
            Conversions::toString(id) +
            ") in ResourceDatabase::getImage().");
    }
    return item->second;
}
